<?php
declare (strict_types=1);

namespace wechat;

use app\admin\model\base\OfficialAccountMenu;
use app\admin\model\base\OfficialAccountMessages;
use app\admin\model\base\User;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Exceptions\HttpException;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Messages\Image;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\NewsItem;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\Video;
use EasyWeChat\Kernel\Messages\Voice;
use GuzzleHttp\Exception\GuzzleException;
use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
use Symfony\Component\HttpFoundation\Response;
use think\facade\Cache;
use think\facade\Log;

/**
 * 无塑地球主体微信公众号
 */
class WsOfficialAccount
{
    //微信公众号
    public object $app;
    public string $name;
    public string $appid;
    public string $secret;
    public string $qrcode;
    public string $token;
    //重定向地址
    public string $redirect_uri;
    //回复消息模板
    public array $newsTemplate = [];

    /**
     * 初始化
     * WechatOfficialAccount constructor.
     */
    public function __construct()
    {
        //获取重定向地址
        $this->redirect_uri = env('ApiDomainV2') . '/scan_bag/wechatJump';
        //回复消息模板
        $this->newsTemplate = [
            'title' => '点我进入领袋流程',
            'description' => '公益环保，为爱蓄力',
            'url' => $this->redirect_uri,
            'image' => env('ApiDomainV2') . '/static/logo/logo.png',
        ];
        //获取系统配置
        $sys_config = config('sys_config.WsOfficialAccount');
        $this->name = $sys_config['name'];
        $this->appid = $sys_config['appid'];
        $this->secret = $sys_config['secret'];
        $this->qrcode = $sys_config['qrcode'];
        $this->token = $sys_config['token'];
        $config = [
            'app_id' => $this->appid,
            'secret' => $this->secret,
            'token' => $this->token,
            'response_type' => 'array',
            'log' => [
                'default' => 'prod',
                'channels' => [
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path' => '/tmp/easywechat.log',
                        'level' => 'info',
                    ],
                ],
            ],
        ];
        $this->app = Factory::officialAccount($config);
    }

    /**
     * 微信公众号验证
     * @return Response
     */
    public function wechatOfficialAccount(): Response
    {
        $response = $this->app->server->serve();
        //将响应输出
        return $response->send();
    }

    /**
     * 微信公众号通过openid获取用户信息
     * @param string $openId
     * @return array
     */
    public function userFromOpenId(string $openId): array
    {
        try {
            $user = $this->app->user->get($openId);
            //将响应输出
            $data = [
                'openid' => $user['openid'],
                'unionid' => $user['unionid'] ?? '',
                'subscribe' => $user['subscribe'] ?? 0,
                'nickname' => $user['nickname'] ?? '',
                'language' => $user['language'] ?? '',
                'avatar' => $user['headimgurl'] ?? '',
            ];
            return ['bool' => true, 'data' => $data, 'msg' => 'ok'];
        } catch (HttpException $e) {
            return ['bool' => false, 'data' => [], 'msg' => $e->getMessage()];
        }
    }

    /**
     * 生成带参数的二维码
     * @param string $sceneValue
     * @return string
     */
    public function temporaryQrcode(string $sceneValue): string
    {
        $tKey = $this->appid . '_temporary_qrcode:' . $sceneValue;
        //判断是否存在缓存
        if (Cache::has($tKey)) {
            $temp = Cache::get($tKey);
        } else {
            $temp = $this->app->qrcode->temporary($sceneValue, 1300000);
            //存入redis
            Cache::set($tKey, $temp, 1296000);
        }
        return $temp['url'];
    }

    /**
     * 处理微信推送消息
     * @return Response
     */
    public function automaticReply(): Response
    {
        $this->app->server->push(function ($message) {
            //定义取袋图文消息
            $NewsItem = [
                new NewsItem($this->newsTemplate),
            ];
            //定义关注的欢迎语
            $returnStr = <<<EOE
感谢您对无塑地球的关注！
如想咨询或了解有关无塑地球的内容
可直接留言公众号
我们会尽快给您回复
或直接拨打客服专线：4009668819
EOE;
            //判断消息类型
            switch ($message['MsgType']) {
                case 'event':
                    //扫码取袋回复
                    if ($message['Event'] == 'SCAN' and $message['EventKey'] == 'scan_bag') {
                        return new News($NewsItem);
                    }
                    //扫码关注取袋回复
                    if ($message['Event'] == 'subscribe' and $message['EventKey'] == 'qrscene_scan_bag') {
                        //写入用户关注
                        User::writeWechatSubscribe($message['FromUserName'], 1);
                        //主动推送消息
                        $this->activePushMessage($message['FromUserName'], $returnStr);
                        return new News($NewsItem);
                    }
                    //用户关注
                    if ($message['Event'] == 'subscribe') {
                        //写入用户关注
                        User::writeWechatSubscribe($message['FromUserName'], 1);
                        //自动回复消息
                        $field = 'messages_type,media_id,type,content';
                        $str = OfficialAccountMessages::where(['type' => 2])->field($field)->find();
                        if ($str) {
                            return $this->messages($str->messages_type, $str->content, $str->media_id);
                        }
                        return new Text($returnStr);
                    }
                    //用户取消关注
                    if ($message['Event'] == 'unsubscribe') {
                        //写入用户关注
                        User::writeWechatSubscribe($message['FromUserName'], 0);
                    }
                    //用户地理位置
                    if ($message['Event'] == 'LOCATION') {
                        //写入用户地理位置
                        //User::writeWechatLocation($message['FromUserName'], $message['Latitude'], $message['Longitude']);
                    }
                    //自定义菜单
                    if ($message['Event'] == 'CLICK') {
                        //自定义菜单回复消息
                        $where = ['key' => $message['EventKey']];
                        $field = 'content,messages_type,media_id,key';
                        $str = OfficialAccountMenu::where($where)->field($field)->find();
                        if (!$str) {
                            //没有匹配到内容，获取自动回复内容
                            $field = 'content,media_id,messages_type,key';
                            $str = OfficialAccountMessages::where(['type' => '3'])->field($field)->find();
                            if (!$str) {
                                return new Text('抱歉，没有找到您要的内容！');
                            }
                        }
                        return $this->messages($str->messages_type, $str->content ?? '', $str->media_id ?? '');
                    }
                    return 'success';
                case 'location':
                    Log::write($message, 'location');
                    break;
                case 'text':
                    //关键字回复
                    $where[] = ['key', 'like', "%{$message['Content']}%"];
                    $field = 'content,media_id,messages_type,key';
                    $str = OfficialAccountMessages::where($where)->field($field)->find();
                    if (!$str) {
                        //没有匹配到内容，获取自动回复内容
                        $str = OfficialAccountMessages::where(['type' => '3'])->field($field)->find();
                        if (!$str) {
                            return new Text('抱歉，没有找到您要的内容！');
                        }
                    }
                    return $this->messages($str->messages_type, $str->content, $str->media_id);
                case 'image':
                case 'voice':
                case 'video':
                case 'link':
                case 'file':
                    break;
            }
            //返回关注的欢迎语
            return $returnStr;
        });
        $response = $this->app->server->serve();
        return $response->send();
    }

    /**
     * 主动推送消息
     * @param string $openId
     * @param string|object $message
     * @return mixed
     */
    public function activePushMessage(string $openId, string|object $message): mixed
    {
        return $this->app->customer_service->message($message)->to($openId)->send();
    }

    /**
     * 发送消息
     * @param string $messages_type
     * @param string $content
     * @param string $media_id
     * @return Image|Text|Video|Voice|void
     */
    private function messages(string $messages_type, string $content, string $media_id)
    {
        if ($messages_type == '1') {
            //文本消息
            if ($content) return new Text($content);
        } elseif ($messages_type == '2') {
            //音频或者视频消息
            if ($media_id) return new Image($media_id);
        } elseif ($messages_type == '3') {
            //音频或者视频消息
            if ($media_id) return new Video($media_id, [
                'title' => '无塑地球',
                'description' => '无塑地球，美丽生活',
            ]);
        } elseif ($messages_type == '4') {
            //音频或者视频消息
            if ($media_id) return new Voice($media_id);
        }
    }

    /**
     * 微信公众号通过code获取用户信息
     * @param string $code
     * @return array
     */
    public function userFromCode(string $code): array
    {
        $oauth = $this->app->oauth;
        //获取OAuth授权结果用户信息
        try {
            $user = $oauth->userFromCode($code);
            $user = $user->toArray();
            $raw = $user['raw'];
            //组装返回数据
            $data = [
                'openid' => $raw['openid'] ?? '',
                'unionid' => $raw['unionid'] ?? '',
                'nickname' => $raw['nickname'] ?? '',
                'avatar' => $raw['headimgurl'] ?? '',
                'sex' => $raw['sex'] ?? 0,
            ];
            return ['bool' => true, 'data' => $data, 'msg' => 'ok'];
        } catch (GuzzleException|AuthorizeFailedException $e) {
            return ['bool' => false, 'data' => [], 'msg' => $e->getMessage()];
        }
    }

    /**
     * 获取jsapi_json配置
     * @param string $url
     * @return array|string
     */
    public function getJsApi(string $url): array|string
    {
        $func_arr = [
            'updateAppMessageShareData',
            'updateTimelineShareData',
            'getLocation'
        ];
        try {
            return $this->app->jssdk->buildConfig($func_arr, false, false, false, [], $url);
        } catch (InvalidArgumentException|InvalidConfigException|RuntimeException|GuzzleException|\Psr\SimpleCache\InvalidArgumentException $e) {
            Log::write($e->getMessage(), '获取jsapi_json配置');
            return [];
        }
    }

    /**
     * 查询已设置的菜单
     * @return array
     * @throws InvalidConfigException
     */
    public function getMenuList(): array
    {
        return $this->app->menu->list();
    }

    /**
     * 获取当前菜单
     * @return array
     * @throws InvalidConfigException
     */
    public function getMenuCurrent(): array
    {
        return $this->app->menu->current();
    }

    /**
     * 创建菜单
     * @param array $bt 菜单数组
     * @return array
     */
    public function createMenu(array $bt): array
    {
        return $this->app->menu->create($bt);
    }

    /**
     * 删除所有菜单
     * @return array
     */
    public function deleteMenuAll(): array
    {
        return $this->app->menu->delete();
    }

    /**
     * 删除指定菜单
     * @param int $meunId
     * @return array
     */
    public function deleteMenuOne(int $meunId): array
    {
        return $this->app->menu->delete($meunId);
    }

    /**
     * 获取当前自动回复设置
     * @return array
     */
    public function getAutoRely(): array
    {
        return $this->app->auto_reply->current();
    }

    /**
     * 素材上传图片
     * @param string $path
     * @return array
     */
    public function uploadImage(string $path): array
    {
        return $this->app->material->uploadImage($path);
    }

    /**
     * 素材上传视频
     * @param string $path
     * @param string $title
     * @return array
     */
    public function uploadVideo(string $path, string $title): array
    {
        return $this->app->material->uploadVideo($path, $title, $title);
    }

    /**
     * 素材上传音频
     * @param string $path
     * @return array
     */
    public function uploadVoice(string $path): array
    {
        return $this->app->material->uploadVoice($path);
    }

}















